<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd">
<html lang="zh-CN"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta http-equiv="Content-Language" content="zh-CN"><link href="stylesheet.css" media="all" rel="stylesheet" type="text/css">
<title>索引扫描</title>
<script> var _hmt = _hmt || []; (function() { var hm = document.createElement("script"); hm.src = "https://hm.baidu.com/hm.js?d286c55b63a3c54a1e43d10d4c203e75"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s); })(); </script>
</head><body class="SECT1">
<div>
<table summary="Header navigation table" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><th colspan="5" align="center" valign="bottom">PostgreSQL 8.2.3 中文文档</th></tr>
<tr><td width="10%" align="left" valign="top"><a href="index-functions.html" accesskey="P">后退</a></td><td width="10%" align="left" valign="top"><a href="indexam.html">快退</a></td><td width="60%" align="center" valign="bottom">章49. 索引访问方法接口定义</td><td width="10%" align="right" valign="top"><a href="indexam.html">快进</a></td><td width="10%" align="right" valign="top"><a href="index-locking.html" accesskey="N">前进</a></td></tr>
</table>
<hr align="LEFT" width="100%"></div>
<div class="SECT1"><h1 class="SECT1"><a name="INDEX-SCANNING">49.3. 索引扫描</a></h1>
<p>在一个索引扫描里，索引访问方法负责把它拿到的那些据说匹配<i class="FIRSTTERM">扫描键字</i>的所有行之 TID 的回流。访问方法<span class="emphasis"><i class="EMPHASIS">不</i></span>会卷入从索引的父表中实际抓取这些行的动作中，也不会判断他们是否通过了扫描的时间条件测试或者是其它条件。</p>
<p>一个扫描键字是形如 <tt class="REPLACEABLE"><i>index_key</i></tt> <tt class="REPLACEABLE"><i>operator</i></tt> <tt class="REPLACEABLE"><i>constant</i></tt> 的 <tt class="LITERAL">WHERE</tt> 子句的内部表现形式，这里的索引键字是索引中的一个字段，而操作符是和该索引字段相关联的操作符类的一个成员。一个索引扫描拥有零个或者多个扫描键字，他们是隐含着 AND 的关系 —返回的行被认为是满所所有列出的条件的行。</p>
<p>操作符类可能会指出改索引对于某些特定的操作符是<i class="FIRSTTERM">有损耗的</i>；这就暗示着该索引扫描会返回所有通过扫描键字的条目，加上一些可能没通过扫描键字的条目。核心系统的索引扫描机制然后就会再次在堆行上使用该操作符，以校验这些条目是否真正应该选取。对于无损耗的操作符，索引扫描必须返回全部匹配的条目，不需要重复校验。</p>
<p>请注意，确保找到所有条目以及确保所有条目都通过给出的扫描键字的条件完全是访问方法的责任。还有，核心系统将只是简单的吧所有匹配扫描键字和操作符类的 <tt class="LITERAL">WHERE</tt> 子句传递过来，而不会做任何语义分析，以判断他们是否冗余或者是否相互矛盾。举例来说，给出 <tt class="LITERAL">WHERE x &gt; 4 AND x &gt; 14</tt> ，这里的 <tt class="LITERAL">x</tt> 是一个 b-tree 索引字段，那么把第一个扫描键字识别成冗余的和可抛弃的工作是 b-tree <code class="FUNCTION">amrescan</code> 函数的事。<code class="FUNCTION">amrescan</code> 过程中所需要的预处理的范围将由索引访问方法把扫描键字缩减为一个"正常"形式的具体需要而定。</p>
<p><code class="FUNCTION">amgettuple</code> 函数有一个 <tt class="LITERAL">direction</tt> 参数，它可以是 <tt class="LITERAL">ForwardScanDirection</tt>(正常情况)或者 <tt class="LITERAL">BackwardScanDirection</tt> 。如果 <code class="FUNCTION">amrescan</code> 之后的第一次调用声明 <tt class="LITERAL">BackwardScanDirection</tt> ，那么匹配条件的索引记录集是从后向前扫描的，而不是通常的从前向后扫描，因此 <code class="FUNCTION">amgettuple</code> 必须返回索引中最后的匹配行，而不是通常情况下的第一条。这些事情只会是那些设置了 <tt class="STRUCTNAME">pg_am</tt>.<tt class="STRUCTFIELD">amorderstrategy</tt>  非零的，号称自己支持排序扫描的访问方法上会发生。在第一次调用之后，<code class="FUNCTION">amgettuple</code> 必须准备从最近返回的条目的位置开始，在两个方向上进行扫描步进。</p>
<p>访问方法必须支持在扫描里"标记"一个位置并且在后面的操作中返回到标记的位置。同样的位置可能会被回复好几次。不过，每次扫描只需要记住一个位置；新的 <code class="FUNCTION">ammarkpos</code> 调用覆盖前面标记的位置。</p>
<p>扫描位置和标记位置(如果存在)都必须在面对索引中存在并发插入和删除的时候保持一致性。如果一条并发新插入的记录并未被一次扫描返回(而如果扫描开始的时候该记录存在，则会被返回)，或者说扫描通过重新扫描或者回头扫描返回这样的记录 —即使它第一次跑的时候没有返回这样的行，对于系统来说，这些情况都是可以接受的。类似的还有，一个并发的删除可以反映，也可以不反应一个扫描的结果。重要的是，插入或者删除不会导致扫描会略过或者重复返回本身不是被插入或者删除的条目。</p>
<p>除了使用 <code class="FUNCTION">amgettuple</code> ，索引扫描页可以用 <code class="FUNCTION">amgetmulti</code> ，每次调用抓取多条行的方式完成。这样做可能会比 <code class="FUNCTION">amgettuple</code> 有显著的效率提升，因为它可以避免在访问方法内的加锁/解锁的循环。在原理上，<code class="FUNCTION">amgetmulti</code> 应该和重复调用 <code class="FUNCTION">amgettuple</code> 的效果相同，不过强制了一些限制来简化事情。首先，<code class="FUNCTION">amgetmulti</code> 并不接受 <tt class="LITERAL">direction</tt> 参数，因此它不支持反向扫描，页不支持内部扫描方向的回向。访问方法也不需要支持在 <code class="FUNCTION">amgetmulti</code> 扫描中扫描位置的标记以及恢复。这些限制几乎没啥开销，因为在 <code class="FUNCTION">amgetmulti</code> 扫描里使用这些特性是很困难的：调整调用着的 TID 缓冲列表是一件很复杂的事情。最后，<code class="FUNCTION">amgetmulti</code> 并不保证在返回的行上的任何锁定，这就暗示着<a href="index-locking.html">节49.4</a>里面的事情。</p>
</div>
<div>
<hr align="LEFT" width="100%">
<table summary="Footer navigation table" width="100%" border="0" cellpadding="0" cellspacing="0">
<tr><td width="33%" align="left" valign="top"><a href="index-functions.html" accesskey="P">后退</a></td><td width="34%" align="center" valign="top"><a href="index.html" accesskey="H">首页</a></td><td width="33%" align="right" valign="top"><a href="index-locking.html" accesskey="N">前进</a></td></tr>
<tr><td width="33%" align="left" valign="top">索引访问方法函数</td><td width="34%" align="center" valign="top"><a href="indexam.html" accesskey="U">上一级</a></td><td width="33%" align="right" valign="top">索引锁的考量</td></tr>
</table>
</div>
</body></html>